package com.zjw.zy.utils.taskpool;

import android.util.Pair;

import androidx.annotation.NonNull;

import com.zjw.zy.smi.JustWithIt;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 *
 * @author ：zhong.jw
 * @date ：Created in 2022/6/10 11:44
 *
 * 可切换线程的holder
 */
public class Holder {

    /**
     * 本工具类提供的线程池
     */
    private static final int MAIN = 0;
    private static final int BACK_GROUND = 1;
    private static final int SEQUENTIAL = 2;
    private static final int GRAPHIC = 3;
    private static final int CONCURRENT = 4;


    private final List<Pair<JustWithIt<ResultContext>, Integer>> mTasks = new ArrayList<>();

    private final Map<Integer, Long> mDelays = new HashMap<>();

    private final ResultContext mContext = new ResultContext();

    private final AtomicInteger mOrder = new AtomicInteger(0);

    /**
     * 一个带并发的抢占式任务池，单例
     */
    public Holder concurrent(@NonNull JustWithIt<ResultContext> task) {
        mTasks.add(new Pair<>(task, CONCURRENT));
        return this;
    }

    /**
     * 一个专门在地图UI层绘制的任务池，单例
     */
    public Holder graphic(@NonNull JustWithIt<ResultContext> task) {
        mTasks.add(new Pair<>(task, GRAPHIC));
        return this;
    }

    /**
     * 一个专门在主线程执行的任务池，单例
     */
    public Holder main(@NonNull JustWithIt<ResultContext> task) {
        mTasks.add(new Pair<>(task, MAIN));
        return this;
    }

    /**
     * 调度BackGround执行的任务池，单例
     */
    public Holder backGround(@NonNull JustWithIt<ResultContext> task) {
        mTasks.add(new Pair<>(task, BACK_GROUND));
        return this;
    }

    /**
     * 串行线程
     */
    public Holder sequential(@NonNull JustWithIt<ResultContext> task) {
        mTasks.add(new Pair<>(task, SEQUENTIAL));
        return this;
    }

    /**
     * 延迟
     */
    public Holder delayed(long delayMillis) {
        int index = mTasks.size();
        Long delay = mDelays.get(index);
        if (delay != null) {
            delay += delayMillis;
        } else {
            delay = delayMillis;
        }

        mDelays.put(index, delay);
        return this;
    }

    /**
     * 执行线程任务串
     * <p>
     * must be use!
     */
    public void execute() {
        realExecute();
    }

    private void realExecute() {
        Pair<JustWithIt<ResultContext>, TaskPool> next = nextTaskAndPool();
        if (next == null) {
            return;
        }

        //提前中断
        if (mContext.getResult() == mContext.mExit) {
            return;
        }

        TaskPool taskPool = next.second;

        Runnable taskInner = () -> {
            JustWithIt<ResultContext> run = next.first;
            run.onWith(mContext);
            //下一次顺序任务
            realExecute();
        };

        Long delayed = mDelays.get(mOrder.get() - 1);
        if (delayed == null) {
            taskPool.post(taskInner);
        } else {
            taskPool.postDelayed(taskInner, delayed);
        }
    }

    private Pair<JustWithIt<ResultContext>, TaskPool> nextTaskAndPool() {
        int index = mOrder.getAndIncrement();

        if (index >= mTasks.size()) {
            return null;
        }
        Pair<JustWithIt<ResultContext>, Integer> next = mTasks.get(index);

        TaskPool pool;
        switch (next.second) {
            case MAIN:
                pool = TaskPool.main();
                break;
            case BACK_GROUND:
                pool = TaskPool.backGround();
                break;
            case SEQUENTIAL:
                pool = TaskPool.sequential();
                break;
            case CONCURRENT:
                pool = TaskPool.concurrent();
                break;
            case GRAPHIC:
                pool = TaskPool.graphic();
                break;
            default:
                return null;
        }
        return new Pair<>(mTasks.get(index).first, pool);
    }

    /**
     * 线程切换值记录上下文
     */
    @SuppressWarnings("unchecked")
    public static class ResultContext {

        private volatile Object mResult = null;

        private final Object mExit = new Object();

        public <T> T getResult() {
            return (T) mResult;
        }

        public <T> void setResult(T result) {
            this.mResult = result;
        }

        public void exit() {
            setResult(mExit);
        }
    }
}
